package ch.abcm.remsmobile.REMSMobile.multicastreceiver;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import ch.abcm.remsmobile.REMSMobile.externnotifier.ExternNotifierController;
import ch.abcm.remsmobile.REMSMobile.externnotifier.ExternNotifierController.MessageType;
import ch.abcm.remsmobile.REMSMobile.orm.DatabaseOpenHelper;
import ch.abcm.remsmobile.REMSMobile.orm.model.Treshhold;
import ch.abcm.remsmobile.REMSMobile.preferences.PrefsActivity;
import ch.bfh.abcm.rems.gateway.GateWayMessageType;
import ch.bfh.abcm.rems.gateway.GatewayMessage;
import ch.bfh.abcm.rems.interfaces.REMSMessageReceiverAdapter;
import ch.bfh.abcm.rems.sensors.tinkerforge.TFSensorType;

import com.j256.ormlite.android.apptools.OpenHelperManager;
import com.j256.ormlite.dao.RuntimeExceptionDao;

public class REMSMulticastReceiver extends REMSMessageReceiverAdapter {

	private static final String TAG = "REMSMulticastReceiver";
	private static final int MIN_AMOUNT_OF_OK = 4;
	private HashMap<TFSensorType, GateWayMessageType> stateMap;
	
	private int okCounter = 0;

	private Context context;

	// private SystemStateController stateController;

	public REMSMulticastReceiver(Context context) {
		stateMap = new HashMap<TFSensorType, GateWayMessageType>();
		this.context = context;
		// this.stateController = new SystemStateController(context);
	}

	@Override
	public void receivedGateWayMessage(GatewayMessage gatewayMessage) {
		Log.i(TAG +" State",
				"Received a GatwayMessage: " + gatewayMessage.getMessageType() + " " + gatewayMessage.getSensorType());
		boolean systemOKBefore = false;
		if (gatewayMessage.getSensorType() != null) {
			// Sensortype of PeriodicNotification is NULL
			systemOKBefore = stateMapAllOK();
			Log.d(TAG +" State", "systemOKBefore: " + systemOKBefore);
			stateMap.put(gatewayMessage.getSensorType(), gatewayMessage.getMessageType());
			Log.d(TAG +" State", "Map: " + stateMap);
		}

		switch (gatewayMessage.getMessageType()) {
		case PERIODIC_NOTIFICATION:
			if (externNotificationIsNecessary(context, gatewayMessage.getMessageType())) {
				sendSimpleExternNotification(context, gatewayMessage.getMessage(), MessageType.PERIODICUPDATE);
			}
			break;
		case MEASUREMENT_DIVERGENCE:
			// its the same behavior
		case SYSTEM_ERROR:
			if (externNotificationIsNecessary(context, gatewayMessage.getMessageType())) {
				Log.d(TAG, "ExternNotification is nessessary");
				sendSimpleExternNotification(context, gatewayMessage.getMessage(), MessageType.SYSTEAM_ERROR);
			} else {
				Log.d(TAG, "ExternNotification is NOT Nessessary, do nothing");
			}
			break;
		case SYSTEM_OK:
			boolean systemOKAfter = stateMapAllOK();
			Log.i(TAG +" State", "systemOKBefore: " + systemOKBefore + " systemOKAfter: " + systemOKAfter);
			// it is more readable than !systemOKBefore
			if ((systemOKBefore == false) && (systemOKAfter == true)) {
				okCounter = 0;
			}
			// it is more readable... 
			if ((systemOKBefore == true)&& (systemOKAfter == true)) {
				if (okCounter <=10) { // prevents from overlap
					okCounter++;
				}
			}
			if (systemOKAfter = false) {
				okCounter = 0;
			}
			
			if (okCounter == MIN_AMOUNT_OF_OK) {
				// if and only if =)
				Log.i(TAG +" State", "System Recovered");
				sendSimpleExternNotification(context, gatewayMessage.getMessage(), MessageType.SYSTEM_OK);
				
			}
			break;
		default:
			Log.e(TAG, "Unknown GatewayMessageType: " + gatewayMessage.getMessageType());
			break;
		}
	}

	private boolean stateMapAllOK() {
		boolean isok = true;
		Iterator<Entry<TFSensorType, GateWayMessageType>> iterator = stateMap.entrySet().iterator();
		while (iterator.hasNext()) {
			Entry<TFSensorType, GateWayMessageType> entry = iterator.next();
			if (entry.getKey() == null) {
				// Sensortype of PeriodicNotification is NULL
				continue;
			}
			isok = isok && entry.getValue().equals(GateWayMessageType.SYSTEM_OK);
		}
		return isok;
	}

	private boolean externNotificationIsNecessary(Context context, GateWayMessageType gateWayMessageType) {
		Log.d(TAG, "externNotificationIsNecessary?");
		DatabaseOpenHelper databaseHelper = OpenHelperManager.getHelper(context, DatabaseOpenHelper.class);
		RuntimeExceptionDao<Treshhold, Integer> dao = databaseHelper.getTreshholdRuntimeExDao();
		Treshhold treshhold = null;
		List<Treshhold> treshholds = dao.queryForEq("GateWayMessageType", gateWayMessageType);
		if (treshholds.size() >= 1) {
			treshhold = treshholds.get(0);
		}
		Log.d(TAG, "Get Treshhold from DB: " + treshhold);
		if (treshhold == null) {
			Log.w(TAG, "Treshold is NULL its the first Time or something went wrong");
			Treshhold newTreshhold = createNewTreshhold(gateWayMessageType);
			dao.create(newTreshhold);
			return true;
		}
		long timeOutInSeconds = treshhold.getTimeout() * 1000;
		long nextMessageTime = treshhold.getTimestampLastMessage() + timeOutInSeconds;
		long now = new Date().getTime();
		Log.d(TAG, "now: " + now + " nextMessageTime: " + nextMessageTime);
		boolean notificationIsNecessary = (now >= nextMessageTime);
		Log.d(TAG, "now >= nextMessageTime: " + notificationIsNecessary);
		if (notificationIsNecessary) {
			treshhold.setTimeout(getTimeoutFromPreferences(gateWayMessageType));
			treshhold.setTimestampLastMessage(now);
			Log.d(TAG, "Notification is necessary, Update Treshhold");
			Log.d("NESS", "Notification is necessary, Update Treshhold Timeout: " + timeOutInSeconds + " > "
					+ (now - nextMessageTime));
			dao.update(treshhold);
			return true;
		}
		Log.d(TAG, "Notification is not necessary");
		Log.d("NESS", "Notification is not necessary Timeout: " + timeOutInSeconds + " > " + (now - nextMessageTime));
		return false;
	}

	private Treshhold createNewTreshhold(GateWayMessageType gateWayMessageType) {
		Log.d(TAG, "Create a new Treshhold");
		long now = new Date().getTime();
		long timeout = getTimeoutFromPreferences(gateWayMessageType);
		Treshhold newTreshhold = new Treshhold(gateWayMessageType, timeout, now);
		Log.d(TAG, "New Treshhold: " + newTreshhold);
		return newTreshhold;
	}

	private long getTimeoutFromPreferences(GateWayMessageType gateWayMessageType) {
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
		try {
			switch (gateWayMessageType) {
			case PERIODIC_NOTIFICATION:
				String t1 = prefs.getString(PrefsActivity.KEY_PREFRENCES_PERIODIC_NOTIFICATION_TIMEOUT,
						PrefsActivity.DEFAULT_TIMEOUT_IN_SECONDS);
				return Long.valueOf(t1);
			case MEASUREMENT_DIVERGENCE:
				String t2 = prefs.getString(PrefsActivity.KEY_PREFRENCES_MEASUREMENT_DIVERGENCE_TIMEOUT,
						PrefsActivity.DEFAULT_TIMEOUT_IN_SECONDS);
				return Long.valueOf(t2);
			case SYSTEM_ERROR:
				String t3 = prefs.getString(PrefsActivity.KEY_PREFRENCES_SYSTEM_ERROR_TIMEOUT,
						PrefsActivity.DEFAULT_TIMEOUT_IN_SECONDS);
				return Long.valueOf(t3);
			case SYSTEM_OK:
				Log.i(TAG, "System Is OK returning 0");
				return 0;
			default:
				Log.e(TAG, "getTimeoutFromPreferences: Unknown Gatewaymessagetype, retunrning 0");
				break;
			}
		} catch (Exception e) {
			Log.e(TAG, "getTimeoutFromPreferences: Unknown Gatewaymessagetype, retunrning 0");
		}
		return 0;
	}

	private void sendSimpleExternNotification(Context context, String message, MessageType messageType) {
		Intent sentPowerNotification = ExternNotifierController.getSimpleMessageIntent(message, messageType);
		Log.d(TAG, "sendSimpleExternNotificationIntent");
		context.sendBroadcast(sentPowerNotification);
	}
	
//	private void sendSimpleExternNotification(Context context, String message) {
//		sendSimpleExternNotification(context, message, MessageType.LOCAL);
//	}
}
